/*-------------------------------------------------------------------------
 *
 *	  转换过程的实用程序函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/utils/mb/conv.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"
#include "mb/pg_wchar.h"


/*
 * local2local：一个通用的单字节字符集编码
 * 在两个 ASCII 超集编码之间转换。
 *
 * l 指向长度为 len 的源字符串
 * p 是输出区（必须足够大！）
 * src_encoding 是源编码的 PG 标识符
 * dest_encoding 是目标编码的 PG 标识符
 * tab 存储源字符集的转换条目
 * 从128（0x80）开始。表中的每个条目保存对应的
 * 目标字符集的代码点，或如果没有等效代码则为 0。
 *
 * 返回消耗的输入字节数。如果 noError 为真，这可能
 * 小于 'len'。
 */
int local2local(const unsigned char *fc_l,
			unsigned char *fc_p,
			int fc_len,
			int fc_src_encoding,
			int fc_dest_encoding,
			const unsigned char *fc_tab,
			bool fc_noError)
{
	const unsigned char *fc_start = fc_l;
	unsigned char fc_c1,
				fc_c2;

	while (fc_len > 0)
	{
		fc_c1 = *fc_l;
		if (fc_c1 == 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(fc_src_encoding, (const char *) fc_l, fc_len);
		}
		if (!IS_HIGHBIT_SET(fc_c1))
			*fc_p++ = fc_c1;
		else
		{
			fc_c2 = fc_tab[fc_c1 - HIGHBIT];
			if (fc_c2)
				*fc_p++ = fc_c2;
			else
			{
				if (fc_noError)
					break;
				report_untranslatable_char(fc_src_encoding, fc_dest_encoding,
										   (const char *) fc_l, fc_len);
			}
		}
		fc_l++;
		fc_len--;
	}
	*fc_p = '\0';

	return fc_l - fc_start;
}

/*
 * LATINn ---> MIC 当字符集的本地代码直接映射到 MIC
 *
 * l 指向长度为 len 的源字符串
 * p 是输出区（必须足够大！）
 * lc 是本地编码的 mule 字符集 ID
 * encoding 是本地编码的 PG 标识符
 *
 * 返回消耗的输入字节数。如果 noError 为真，这可能
 * 小于 'len'。
 */
int latin2mic(const unsigned char *fc_l, unsigned char *fc_p, int fc_len,
		  int fc_lc, int fc_encoding, bool fc_noError)
{
	const unsigned char *fc_start = fc_l;
	int			fc_c1;

	while (fc_len > 0)
	{
		fc_c1 = *fc_l;
		if (fc_c1 == 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(fc_encoding, (const char *) fc_l, fc_len);
		}
		if (IS_HIGHBIT_SET(fc_c1))
			*fc_p++ = fc_lc;
		*fc_p++ = fc_c1;
		fc_l++;
		fc_len--;
	}
	*fc_p = '\0';

	return fc_l - fc_start;
}

/*
 * MIC ---> LATINn 当字符集的本地代码直接映射到 MIC
 *
 * mic 指向长度为 len 的源字符串
 * p 是输出区（必须足够大！）
 * lc 是本地编码的 mule 字符集 ID
 * encoding 是本地编码的 PG 标识符
 *
 * 返回消耗的输入字节数。如果 noError 为真，这可能
 * 小于 'len'。
 */
int mic2latin(const unsigned char *fc_mic, unsigned char *fc_p, int fc_len,
		  int fc_lc, int fc_encoding, bool fc_noError)
{
	const unsigned char *fc_start = fc_mic;
	int			fc_c1;

	while (fc_len > 0)
	{
		fc_c1 = *fc_mic;
		if (fc_c1 == 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(PG_MULE_INTERNAL, (const char *) fc_mic, fc_len);
		}
		if (!IS_HIGHBIT_SET(fc_c1))
		{
			/* 对于 ASCII 容易 */
			*fc_p++ = fc_c1;
			fc_mic++;
			fc_len--;
		}
		else
		{
			int			fc_l = pg_mule_mblen(fc_mic);

			if (fc_len < fc_l)
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_MULE_INTERNAL, (const char *) fc_mic,
										fc_len);
			}
			if (fc_l != 2 || fc_c1 != fc_lc || !IS_HIGHBIT_SET(fc_mic[1]))
			{
				if (fc_noError)
					break;
				report_untranslatable_char(PG_MULE_INTERNAL, fc_encoding,
										   (const char *) fc_mic, fc_len);
			}
			*fc_p++ = fc_mic[1];
			fc_mic += 2;
			fc_len -= 2;
		}
	}
	*fc_p = '\0';

	return fc_mic - fc_start;
}


/*
 * latin2mic_with_table：一个通用的单字节字符集编码
 * 从本地字符集转换到 mule 内部代码。
 *
 * l 指向长度为 len 的源字符串
 * p 是输出区（必须足够大！）
 * lc 是本地编码的 mule 字符集 ID
 * encoding 是本地编码的 PG 标识符
 * tab 存储本地字符集的转换条目
 * 从128（0x80）开始。表中的每个条目保存对应的
 * mule 编码的代码点，或如果没有等效代码则为 0。
 *
 * 返回消耗的输入字节数。如果 noError 为真，这可能
 * 小于 'len'。
 */
int latin2mic_with_table(const unsigned char *fc_l,
					 unsigned char *fc_p,
					 int fc_len,
					 int fc_lc,
					 int fc_encoding,
					 const unsigned char *fc_tab,
					 bool fc_noError)
{
	const unsigned char *fc_start = fc_l;
	unsigned char fc_c1,
				fc_c2;

	while (fc_len > 0)
	{
		fc_c1 = *fc_l;
		if (fc_c1 == 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(fc_encoding, (const char *) fc_l, fc_len);
		}
		if (!IS_HIGHBIT_SET(fc_c1))
			*fc_p++ = fc_c1;
		else
		{
			fc_c2 = fc_tab[fc_c1 - HIGHBIT];
			if (fc_c2)
			{
				*fc_p++ = fc_lc;
				*fc_p++ = fc_c2;
			}
			else
			{
				if (fc_noError)
					break;
				report_untranslatable_char(fc_encoding, PG_MULE_INTERNAL,
										   (const char *) fc_l, fc_len);
			}
		}
		fc_l++;
		fc_len--;
	}
	*fc_p = '\0';

	return fc_l - fc_start;
}

/*
 * mic2latin_with_table：一个通用的单字节字符集编码
 * 从 mule 内部代码转换到本地字符集。
 *
 * mic 指向长度为 len 的源字符串
 * p 是输出区（必须足够大！）
 * lc 是本地编码的 mule 字符集 ID
 * encoding 是本地编码的 PG 标识符
 * tab 存储 mule 内部代码的第二个字节的转换条目，
 * 从128（0x80）开始。表中的每个条目保存对应的
 * 本地字符集的代码点，或如果没有等效代码则为 0。
 *
 * 返回消耗的输入字节数。如果 noError 为真，这可能
 * 小于 'len'。
 */
int mic2latin_with_table(const unsigned char *fc_mic,
					 unsigned char *fc_p,
					 int fc_len,
					 int fc_lc,
					 int fc_encoding,
					 const unsigned char *fc_tab,
					 bool fc_noError)
{
	const unsigned char *fc_start = fc_mic;
	unsigned char fc_c1,
				fc_c2;

	while (fc_len > 0)
	{
		fc_c1 = *fc_mic;
		if (fc_c1 == 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(PG_MULE_INTERNAL, (const char *) fc_mic, fc_len);
		}
		if (!IS_HIGHBIT_SET(fc_c1))
		{
			/* 对于 ASCII 容易 */
			*fc_p++ = fc_c1;
			fc_mic++;
			fc_len--;
		}
		else
		{
			int			fc_l = pg_mule_mblen(fc_mic);

			if (fc_len < fc_l)
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_MULE_INTERNAL, (const char *) fc_mic,
										fc_len);
			}
			if (fc_l != 2 || fc_c1 != fc_lc || !IS_HIGHBIT_SET(fc_mic[1]) ||
				(fc_c2 = fc_tab[fc_mic[1] - HIGHBIT]) == 0)
			{
				if (fc_noError)
					break;
				report_untranslatable_char(PG_MULE_INTERNAL, fc_encoding,
										   (const char *) fc_mic, fc_len);
				break;			/* 保持编译器安静 */
			}
			*fc_p++ = fc_c2;
			fc_mic += 2;
			fc_len -= 2;
		}
	}
	*fc_p = '\0';

	return fc_mic - fc_start;
}

/*
 * bsearch() 的比较例程
 * 此例程用于组合 UTF8 -> 本地代码
 */
static int fc_compare3(const void *fc_p1, const void *fc_p2)
{
	uint32		fc_s1,
				fc_s2,
				fc_d1,
				fc_d2;

	fc_s1 = *(const uint32 *) fc_p1;
	fc_s2 = *((const uint32 *) fc_p1 + 1);
	fc_d1 = ((const pg_utf_to_local_combined *) fc_p2)->utf1;
	fc_d2 = ((const pg_utf_to_local_combined *) fc_p2)->utf2;
	return (fc_s1 > fc_d1 || (fc_s1 == fc_d1 && fc_s2 > fc_d2)) ? 1 : ((fc_s1 == fc_d1 && fc_s2 == fc_d2) ? 0 : -1);
}

/*
 * bsearch() 的比较例程
 * 此例程用于本地代码 -> 组合 UTF8
 */
static int fc_compare4(const void *fc_p1, const void *fc_p2)
{
	uint32		fc_v1,
				fc_v2;

	fc_v1 = *(const uint32 *) fc_p1;
	fc_v2 = ((const pg_local_to_utf_combined *) fc_p2)->code;
	return (fc_v1 > fc_v2) ? 1 : ((fc_v1 == fc_v2) ? 0 : -1);
}

/*
 * 将 32 位字符表示存储到多字节流中
 */
static inline unsigned char *
fc_store_coded_char(unsigned char *fc_dest, uint32 fc_code)
{
	if (fc_code & 0xff000000)
		*fc_dest++ = fc_code >> 24;
	if (fc_code & 0x00ff0000)
		*fc_dest++ = fc_code >> 16;
	if (fc_code & 0x0000ff00)
		*fc_dest++ = fc_code >> 8;
	if (fc_code & 0x000000ff)
		*fc_dest++ = fc_code;
	return fc_dest;
}

/*
 * 使用转换基数树转换字符。
 *
 * 'l' 是输入字符的字节长度，b1-b4 是
 * 输入字符的字节。
 */
static inline uint32 pg_mb_radix_conv(const pg_mb_radix_tree *fc_rt,
				 int fc_l,
				 unsigned char fc_b1,
				 unsigned char fc_b2,
				 unsigned char fc_b3,
				 unsigned char fc_b4)
{
	if (fc_l == 4)
	{
		/* 4 字节代码 */

		/* 检查代码有效性 */
		if (fc_b1 < fc_rt->b4_1_lower || fc_b1 > fc_rt->b4_1_upper ||
			fc_b2 < fc_rt->b4_2_lower || fc_b2 > fc_rt->b4_2_upper ||
			fc_b3 < fc_rt->b4_3_lower || fc_b3 > fc_rt->b4_3_upper ||
			fc_b4 < fc_rt->b4_4_lower || fc_b4 > fc_rt->b4_4_upper)
			return 0;

		/* 执行查找 */
		if (fc_rt->chars32)
		{
			uint32		fc_idx = fc_rt->b4root;

			fc_idx = fc_rt->chars32[fc_b1 + fc_idx - fc_rt->b4_1_lower];
			fc_idx = fc_rt->chars32[fc_b2 + fc_idx - fc_rt->b4_2_lower];
			fc_idx = fc_rt->chars32[fc_b3 + fc_idx - fc_rt->b4_3_lower];
			return fc_rt->chars32[fc_b4 + fc_idx - fc_rt->b4_4_lower];
		}
		else
		{
			uint16		fc_idx = fc_rt->b4root;

			fc_idx = fc_rt->chars16[fc_b1 + fc_idx - fc_rt->b4_1_lower];
			fc_idx = fc_rt->chars16[fc_b2 + fc_idx - fc_rt->b4_2_lower];
			fc_idx = fc_rt->chars16[fc_b3 + fc_idx - fc_rt->b4_3_lower];
			return fc_rt->chars16[fc_b4 + fc_idx - fc_rt->b4_4_lower];
		}
	}
	else if (fc_l == 3)
	{
		/* 3 字节代码 */

		/* 检查代码有效性 */
		if (fc_b2 < fc_rt->b3_1_lower || fc_b2 > fc_rt->b3_1_upper ||
			fc_b3 < fc_rt->b3_2_lower || fc_b3 > fc_rt->b3_2_upper ||
			fc_b4 < fc_rt->b3_3_lower || fc_b4 > fc_rt->b3_3_upper)
			return 0;

		/* 执行查找 */
		if (fc_rt->chars32)
		{
			uint32		fc_idx = fc_rt->b3root;

			fc_idx = fc_rt->chars32[fc_b2 + fc_idx - fc_rt->b3_1_lower];
			fc_idx = fc_rt->chars32[fc_b3 + fc_idx - fc_rt->b3_2_lower];
			return fc_rt->chars32[fc_b4 + fc_idx - fc_rt->b3_3_lower];
		}
		else
		{
			uint16		fc_idx = fc_rt->b3root;

			fc_idx = fc_rt->chars16[fc_b2 + fc_idx - fc_rt->b3_1_lower];
			fc_idx = fc_rt->chars16[fc_b3 + fc_idx - fc_rt->b3_2_lower];
			return fc_rt->chars16[fc_b4 + fc_idx - fc_rt->b3_3_lower];
		}
	}
	else if (fc_l == 2)
	{
		/* 2 字节代码 */

		/* 检查代码有效性 - 第一个字节 */
		if (fc_b3 < fc_rt->b2_1_lower || fc_b3 > fc_rt->b2_1_upper ||
			fc_b4 < fc_rt->b2_2_lower || fc_b4 > fc_rt->b2_2_upper)
			return 0;

		/* 执行查找 */
		if (fc_rt->chars32)
		{
			uint32		fc_idx = fc_rt->b2root;

			fc_idx = fc_rt->chars32[fc_b3 + fc_idx - fc_rt->b2_1_lower];
			return fc_rt->chars32[fc_b4 + fc_idx - fc_rt->b2_2_lower];
		}
		else
		{
			uint16		fc_idx = fc_rt->b2root;

			fc_idx = fc_rt->chars16[fc_b3 + fc_idx - fc_rt->b2_1_lower];
			return fc_rt->chars16[fc_b4 + fc_idx - fc_rt->b2_2_lower];
		}
	}
	else if (fc_l == 1)
	{
		/* 1字节代码 */

		/* 检查代码有效性 - 第一个字节 */
		if (fc_b4 < fc_rt->b1_lower || fc_b4 > fc_rt->b1_upper)
			return 0;

		/* 执行查找 */
		if (fc_rt->chars32)
			return fc_rt->chars32[fc_b4 + fc_rt->b1root - fc_rt->b1_lower];
		else
			return fc_rt->chars16[fc_b4 + fc_rt->b1root - fc_rt->b1_lower];
	}
	return 0;					/* 不应该发生 */
}

/*
 * UTF8 ---> local code
 *
 * utf: 输入字符串，采用UTF8编码（不需要以null结尾）
 * len: 输入字符串的长度（以字节为单位）
 * iso: 指向输出区域的指针（必须足够大！）
		  （输出字符串将以null结尾）
 * map: 单字符的转换映射表
 * cmap: 组合字符的转换映射表
 *		  （可选，如果没有则传入NULL）
 * cmapsize: 组合字符的转换映射表中的条目数
 *		  （可选，如果没有则传入0）
 * conv_func: 算法编码转换函数
 *		  （可选，如果没有则传入NULL）
 * encoding: 本地编码的PG标识符
 *
 * 对于每个字符，首先查询cmap（如果提供）；如果没有匹配，
 * 接下来查询map；如果仍然没有匹配，应用conv_func（如果提供）。
 * 如果未找到匹配，则会抛出错误。
 *
 * 有关所使用数据结构的更多详细信息，请参见pg_wchar.h。
 *
 * 返回消耗的输入字节数。如果noError为true，这个值可能
 * 小于'len'。
 */
int UtfToLocal(const unsigned char *fc_utf, int fc_len,
		   unsigned char *fc_iso,
		   const pg_mb_radix_tree *fc_map,
		   const pg_utf_to_local_combined *fc_cmap, int fc_cmapsize,
		   utf_local_conversion_func fc_conv_func,
		   int fc_encoding, bool fc_noError)
{
	uint32		fc_iutf;
	int			fc_l;
	const pg_utf_to_local_combined *fc_cp;
	const unsigned char *fc_start = fc_utf;

	if (!PG_VALID_ENCODING(fc_encoding))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid encoding number: %d", fc_encoding)));

	for (; fc_len > 0; fc_len -= fc_l)
	{
		unsigned char fc_b1 = 0;
		unsigned char fc_b2 = 0;
		unsigned char fc_b3 = 0;
		unsigned char fc_b4 = 0;

		/* "break"情况都表示错误 */
		if (*fc_utf == '\0')
			break;

		fc_l = pg_utf_mblen(fc_utf);
		if (fc_len < fc_l)
			break;

		if (!pg_utf8_islegal(fc_utf, fc_l))
			break;

		if (fc_l == 1)
		{
			/* ASCII情况很简单，假设是逐一转换 */
			*fc_iso++ = *fc_utf++;
			continue;
		}

		/* 收集长度为l的编码字符 */
		if (fc_l == 2)
		{
			fc_b3 = *fc_utf++;
			fc_b4 = *fc_utf++;
		}
		else if (fc_l == 3)
		{
			fc_b2 = *fc_utf++;
			fc_b3 = *fc_utf++;
			fc_b4 = *fc_utf++;
		}
		else if (fc_l == 4)
		{
			fc_b1 = *fc_utf++;
			fc_b2 = *fc_utf++;
			fc_b3 = *fc_utf++;
			fc_b4 = *fc_utf++;
		}
		else
		{
			elog(ERROR, "unsupported character length %d", fc_l);
			fc_iutf = 0;			/* 保持编译器安静 */
		}
		fc_iutf = (fc_b1 << 24 | fc_b2 << 16 | fc_b3 << 8 | fc_b4);

		/* 首先，尽量尝试组合映射 */
		if (fc_cmap && fc_len > fc_l)
		{
			const unsigned char *fc_utf_save = fc_utf;
			int			fc_len_save = fc_len;
			int			fc_l_save = fc_l;

			/* 收集下一个字符，和上面一样 */
			fc_len -= fc_l;

			fc_l = pg_utf_mblen(fc_utf);
			if (fc_len < fc_l)
			{
				/* 需要更多数据来决定这是否是一个组合字符 */
				fc_utf -= fc_l_save;
				break;
			}

			if (!pg_utf8_islegal(fc_utf, fc_l))
			{
				if (!fc_noError)
					report_invalid_encoding(PG_UTF8, (const char *) fc_utf, fc_len);
				fc_utf -= fc_l_save;
				break;
			}

			/* 我们假设ASCII字符不能出现在组合映射中 */
			if (fc_l > 1)
			{
				uint32		fc_iutf2;
				uint32		fc_cutf[2];

				if (fc_l == 2)
				{
					fc_iutf2 = *fc_utf++ << 8;
					fc_iutf2 |= *fc_utf++;
				}
				else if (fc_l == 3)
				{
					fc_iutf2 = *fc_utf++ << 16;
					fc_iutf2 |= *fc_utf++ << 8;
					fc_iutf2 |= *fc_utf++;
				}
				else if (fc_l == 4)
				{
					fc_iutf2 = *fc_utf++ << 24;
					fc_iutf2 |= *fc_utf++ << 16;
					fc_iutf2 |= *fc_utf++ << 8;
					fc_iutf2 |= *fc_utf++;
				}
				else
				{
					elog(ERROR, "unsupported character length %d", fc_l);
					fc_iutf2 = 0;	/* 保持编译器安静 */
				}

				fc_cutf[0] = fc_iutf;
				fc_cutf[1] = fc_iutf2;

				fc_cp = bsearch(fc_cutf, fc_cmap, fc_cmapsize,
							 sizeof(pg_utf_to_local_combined), fc_compare3);

				if (fc_cp)
				{
					fc_iso = fc_store_coded_char(fc_iso, fc_cp->code);
					continue;
				}
			}

			/* 失败，所以回退到重新处理第二个字符 */
			fc_utf = fc_utf_save;
			fc_len = fc_len_save;
			fc_l = fc_l_save;
		}

		/* 现在检查普通映射 */
		if (fc_map)
		{
			uint32		fc_converted = pg_mb_radix_conv(fc_map, fc_l, fc_b1, fc_b2, fc_b3, fc_b4);

			if (fc_converted)
			{
				fc_iso = fc_store_coded_char(fc_iso, fc_converted);
				continue;
			}
		}

		/* 如果有转换函数，尝试一下 */
		if (fc_conv_func)
		{
			uint32		fc_converted = (*fc_conv_func) (fc_iutf);

			if (fc_converted)
			{
				fc_iso = fc_store_coded_char(fc_iso, fc_converted);
				continue;
			}
		}

		/* 无法翻译这个字符 */
		fc_utf -= fc_l;
		if (fc_noError)
			break;
		report_untranslatable_char(PG_UTF8, fc_encoding,
								   (const char *) fc_utf, fc_len);
	}

	/* 如果我们提前跳出循环，必须是无效输入 */
	if (fc_len > 0 && !fc_noError)
		report_invalid_encoding(PG_UTF8, (const char *) fc_utf, fc_len);

	*fc_iso = '\0';

	return fc_utf - fc_start;
}

/*
 * local code ---> UTF8
 *
 * iso: 输入字符串，采用本地编码（不需要以null结尾）
 * len: 输入字符串的长度（以字节为单位）
 * utf: 指向输出区域的指针（必须足够大！）
		  （输出字符串将以null结尾）
 * map: 单字符的转换映射表
 * cmap: 组合字符的转换映射表
 *		  （可选，如果没有则传入NULL）
 * cmapsize: 组合字符的转换映射表中的条目数
 *		  （可选，如果没有则传入0）
 * conv_func: 算法编码转换函数
 *		  （可选，如果没有则传入NULL）
 * encoding: 本地编码的PG标识符
 *
 * 对于每个字符，首先查询map；如果没有匹配，接下来查询cmap
 * （如果提供）；如果仍然没有匹配，应用conv_func
 * （如果提供）。如果未找到匹配，则会抛出错误。
 *
 * 有关所使用数据结构的更多详细信息，请参见pg_wchar.h。
 *
 * 返回消耗的输入字节数。如果noError为true，这个值可能
 * 小于'len'。
 */
int LocalToUtf(const unsigned char *fc_iso, int fc_len,
		   unsigned char *fc_utf,
		   const pg_mb_radix_tree *fc_map,
		   const pg_local_to_utf_combined *fc_cmap, int fc_cmapsize,
		   utf_local_conversion_func fc_conv_func,
		   int fc_encoding,
		   bool fc_noError)
{
	uint32		fc_iiso;
	int			fc_l;
	const pg_local_to_utf_combined *fc_cp;
	const unsigned char *fc_start = fc_iso;

	if (!PG_VALID_ENCODING(fc_encoding))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid encoding number: %d", fc_encoding)));

	for (; fc_len > 0; fc_len -= fc_l)
	{
		unsigned char fc_b1 = 0;
		unsigned char fc_b2 = 0;
		unsigned char fc_b3 = 0;
		unsigned char fc_b4 = 0;

		/* "break"情况都表示错误 */
		if (*fc_iso == '\0')
			break;

		if (!IS_HIGHBIT_SET(*fc_iso))
		{
			/* ASCII情况很简单，假设是逐一转换 */
			*fc_utf++ = *fc_iso++;
			fc_l = 1;
			continue;
		}

		fc_l = pg_encoding_verifymbchar(fc_encoding, (const char *) fc_iso, fc_len);
		if (fc_l < 0)
			break;

		/* 收集长度为l的编码字符 */
		if (fc_l == 1)
			fc_b4 = *fc_iso++;
		else if (fc_l == 2)
		{
			fc_b3 = *fc_iso++;
			fc_b4 = *fc_iso++;
		}
		else if (fc_l == 3)
		{
			fc_b2 = *fc_iso++;
			fc_b3 = *fc_iso++;
			fc_b4 = *fc_iso++;
		}
		else if (fc_l == 4)
		{
			fc_b1 = *fc_iso++;
			fc_b2 = *fc_iso++;
			fc_b3 = *fc_iso++;
			fc_b4 = *fc_iso++;
		}
		else
		{
			elog(ERROR, "unsupported character length %d", fc_l);
			fc_iiso = 0;			/* 保持编译器安静 */
		}
		fc_iiso = (fc_b1 << 24 | fc_b2 << 16 | fc_b3 << 8 | fc_b4);

		if (fc_map)
		{
			uint32		fc_converted = pg_mb_radix_conv(fc_map, fc_l, fc_b1, fc_b2, fc_b3, fc_b4);

			if (fc_converted)
			{
				fc_utf = fc_store_coded_char(fc_utf, fc_converted);
				continue;
			}

			/* 如果有组合字符映射，尝试一下 */
			if (fc_cmap)
			{
				fc_cp = bsearch(&fc_iiso, fc_cmap, fc_cmapsize,
							 sizeof(pg_local_to_utf_combined), fc_compare4);

				if (fc_cp)
				{
					fc_utf = fc_store_coded_char(fc_utf, fc_cp->utf1);
					fc_utf = fc_store_coded_char(fc_utf, fc_cp->utf2);
					continue;
				}
			}
		}

		/* 如果有转换函数，尝试一下 */
		if (fc_conv_func)
		{
			uint32		fc_converted = (*fc_conv_func) (fc_iiso);

			if (fc_converted)
			{
				fc_utf = fc_store_coded_char(fc_utf, fc_converted);
				continue;
			}
		}

		/* 无法翻译这个字符 */
		fc_iso -= fc_l;
		if (fc_noError)
			break;
		report_untranslatable_char(fc_encoding, PG_UTF8,
								   (const char *) fc_iso, fc_len);
	}

	/* 如果我们提前跳出循环，必须是无效输入 */
	if (fc_len > 0 && !fc_noError)
		report_invalid_encoding(fc_encoding, (const char *) fc_iso, fc_len);

	*fc_utf = '\0';

	return fc_iso - fc_start;
}
